package com.hmdp.service.impl;

import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisConstants;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
@Slf4j
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
//    @Autowired
    private ISeckillVoucherService iSeckillVoucherService;
//    @Autowired
    private RedisIdWorker redisIdWorker;
//    @Autowired
    private StringRedisTemplate stringRedisTemplate;
//    我发现一个不适合使用构造函数注入的场景 也就是说当需要注入的 比较多的时候需要注意不能使用构造函数注入 使用@Resource
    public VoucherOrderServiceImpl(ISeckillVoucherService iSeckillVoucherService, RedisIdWorker redisIdWorker, StringRedisTemplate stringRedisTemplate, RedissonClient redissonClient) {
        this.iSeckillVoucherService = iSeckillVoucherService;
        this.redisIdWorker = redisIdWorker;
        this.stringRedisTemplate = stringRedisTemplate;
        this.redissonClient = redissonClient;
    }
//    @Resource
    private RedissonClient redissonClient;

//    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
//
//    static {
//        SECKILL_SCRIPT = new DefaultRedisScript<>();
//        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
//    }

//    这个是管理线程的接口 提供了一种执行异步任务的方式 可以控制任务的执行 提交新任务以及获取任务的执行结果
//    通过 ExecutorService，可以方便地管理线程池，控制并发执行的任务数量，以及处理任务执行过程中的异常情况。
//    private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();

//    @PostConstruct
//    private void init(){
//        SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());
//    }
//    private BlockingQueue<VoucherOrder> blockingQueue = new ArrayBlockingQueue<>(1024 * 1024);

//    @Autowired
    private PlatformTransactionManager transactionManager;

    private IVoucherOrderService proxy;
//    private class VoucherOrderHandler implements  Runnable{
//        @Override
//        public void run() {
//            while(true){
//                try {
//                    VoucherOrder take = blockingQueue.take();
//                    handlerVoucherOrder(take);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//
//            }
//        }
//
//        private void handlerVoucherOrder(VoucherOrder voucherOrder) {
//            Long userId = voucherOrder.getUserId();
//            RLock rLock = redissonClient.getLock("lock:order:"+userId);
//            boolean isLock = rLock.tryLock();
//            if (!isLock) {
//                log.error("不允许重复下单");
//                return;
//            }
//
//            try{
//                return proxy.creatVoucherOrder(voucherOrder);
//            }catch (Exception e){
//                rLock.unlock();
//            }
//
//        }
//    }
    // 阻塞队列

    //    异步秒杀优化
//    public Result seckilVoucher(Long voucherId) {
//        // 1. 执行lua脚本
//        Long id =  UserHolder.getUser().getId();
//        Long result = stringRedisTemplate.execute(SECKILL_SCRIPT, Collections.emptyList(), voucherId.toString(),id.toString());
//        int r = result.intValue();
//        if(r!=0){
//            return Result.fail(r == 1?"库存不足":"不能重复下单");
//        }
//        VoucherOrder voucherOrder = new VoucherOrder();
//        long orderId = redisIdWorker.nextId("order");
//        voucherOrder.setId(orderId);
//        voucherOrder.setUserId(id);
//        voucherOrder.setVoucherId(voucherId);
//        blockingQueue.add(voucherOrder);
//        // 获取代理对象
//
//        // 进行处理
//        return Result.ok(orderId);
//    }//    @Override
    // 这个回滚很灵性
    public Result seckilVoucher(Long voucherId) {
        SeckillVoucher byId = iSeckillVoucherService.getById(voucherId);
        if (byId.getBeginTime().isAfter(LocalDateTime.now())) {
            return Result.fail("秒杀尚未开始");
        }
        if (byId.getEndTime().isBefore(LocalDateTime.now())) {
            return Result.fail("秒杀活动已结束");
        }
        if (byId.getStock() < 1) {
            return Result.fail("库存不足");
        }
        // 保证唯一值 同步代码块 其中参数是 多线程中的唯一值
        /*
         *   会存在问题
         *      先获取锁 再提交事务 再释放锁
         *
         *   问题代码
         *    public Result creatVoucherOrder(Long voucherId) {
         *         这个里面获取用户id
         *          然后加锁 来控制这部分内容
         *          这样容易造成 用户此时释放锁之后 但是事务还没提交 造成问题
         * }
         * */
        UserDTO user1 = UserHolder.getUser();
        //        synchronized (user1.toString().intern()) {
        ////            解决 通过代理进行调用
        //            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
        ////            此时需要导入依赖
        //                /*
        //                    <dependency>
        //                        <groupId>org.aspectj</groupId>
        //                        <artifactId>aspectjweaver</artifactId>
        //                    </dependency>
        //                * */
        ////            在启动类上添加注解暴露代理@EnableAspectJAutoProxy(exposeProxy = true)
        ////            直接返回的时候没有事务功能因为是this.creatVoucherOrder(voucherId);
        ////            事务处理事当前的这个类做了动态代理 拿到代理对象做事务代理才会生效
        //            return proxy.creatVoucherOrder(voucherId);
        //        }
        /*
         *
         *   分布式锁
         *   用的还是Redis
         * */
        //        SimpleRedisLock simpleRedisLock = new SimpleRedisLock(stringRedisTemplate, ("order:" + user1.getId()));
        //        boolean isLock = simpleRedisLock.tryLock(10L);
//        这个是防止一个账号多台设备登录抢票的吗？
        RLock lock = redissonClient.getLock("order:" + user1.getId());
        boolean isLock = lock.tryLock();
        if (!isLock) {
            return Result.fail("不允许重复下单");
        }
        try {
//                也可以直接创建一个事务对象 然后提交这个内容 也是可以的
//            TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
//            creatVoucherOrder(voucherId);
//            transactionManager.commit(status);
            proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.creatVoucherOrder(voucherId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    /*
     *
     *   实现一人一单的内容
     *
     * */
    @Transactional
    public Result creatVoucherOrder(Long voucherId) {
        UserDTO user = UserHolder.getUser();
//        这位置有问题吧，难道我一个店只能抢一张劵？
        String keyUserOrder = "user:order:" + voucherId + user.getId();

        // 一人一单 使用lua脚本判断订单
        Long id = UserHolder.getUser().getId();
//        Integer count = query().eq("user_id", id).eq("voucher_id", voucherId).count();
        Integer count = 0;
        try{
            count  = Integer.valueOf(stringRedisTemplate.opsForValue().get(keyUserOrder));
            if (count >= 1) {
                return Result.fail("用户已购买");
            }
        }catch (Exception e){
            log.error("第一次抢票");
        }
        // 实现锁的内容 这个位置只要库存大于0就可以强 否则不能抢 这样就避免了抢票失败的情况
//        这个也是操作数据库的内容 这个只需要更新即可 判断就直接用redis判断
//        获取库存
//        Long s = Long.valueOf(stringRedisTemplate.opsForValue().get(RedisConstants.SECKILL_STOCK_KEY + voucherId));
//        stringRedisTemplate.opsForValue().set();
        boolean voucherId1 = iSeckillVoucherService.update()
                .setSql("stock = stock - 1")
                .eq("voucher_id", voucherId)
                .gt("stock", 0)
                .update();
        // 创建订单
        if (!voucherId1) {
            return Result.fail("库存不足");
        }
        VoucherOrder voucherOrder = new VoucherOrder();

        Long orderID = redisIdWorker.nextId("order");
//        订单id
        voucherOrder.setId(orderID);
        voucherOrder.setUserId(user.getId());
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);
//        也存redis
        // 自增一
        Long orderCount = stringRedisTemplate.opsForValue().increment(keyUserOrder, 1);
//        返回成功信息
        return Result.ok(orderID);
    }
//    这个是异步使用
//    @Transactional
//    public void creatVoucherOrder(VoucherOrder voucherId) {
//        Long id = UserHolder.getUser().getId();
//        Integer count = query().eq("user_id", id).eq("voucher_id", voucherId).count();
//        if (count >= 1) {
////            return Result.fail("用户已购买");
//            return;
//        }
//
//        boolean voucherId1 = iSeckillVoucherService.update()
//                .setSql("stock = stock - 1")
//                .eq("voucher_id", voucherId)
//                /*
//                 *  乐观锁
//                 *  where id = ? and stock = ?
//                 * */
//                .gt("stock", 0)
//                .update();
//        // 创建订单
//        if (!voucherId1) {
////            return Result.fail("库存不足");
//            return;
//        }
//
//        Long orderID = redisIdWorker.nextId("order");
//
//        save(voucherId);
//    }
}
